package com.android.samples.mediastore.hms.drive.operate

import android.text.TextUtils
import android.util.Log
import androidx.lifecycle.MutableLiveData
import com.android.samples.mediastore.hms.drive.utils.MimeType
import com.huawei.cloud.base.http.FileContent
import com.huawei.cloud.base.http.InputStreamContent
import com.huawei.cloud.base.media.MediaHttpDownloaderProgressListener
import com.huawei.cloud.base.util.DateTime
import com.huawei.cloud.base.util.StringUtils
import com.huawei.cloud.base.util.base64.Base64
import com.huawei.cloud.services.drive.Drive
import com.huawei.cloud.services.drive.model.Channel
import com.huawei.cloud.services.drive.model.File
import com.huawei.cloud.services.drive.model.File.ContentExtras
import com.huawei.cloud.services.drive.model.File.ContentExtras.Thumbnail
import com.huawei.hms.utils.HEX
import com.huawei.hms.utils.SHA256
import kotlinx.coroutines.*
import kotlinx.coroutines.scheduling.*
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executors
import javax.inject.Inject
import javax.inject.Singleton


/**
 * 华为云空间服务的核心能力是对云端文件的管理,
 * 用户可以通过华为云空间服务方便快捷的管理、编辑华为云盘上的文件，支持创建、获取文件列表、拷贝、修改文件、移入回收站、清空回收站文件及搜索云盘上的文件等能力。
 */

@Singleton
class FilesManager @Inject constructor(private val drive: Drive) {
    val TAG = "FilesManager"
    private val DIRECT_UPLOAD_MAX_SIZE = 20 * 1024 * 1024
    private val DIRECT_DOWNLOAD_MAX_SIZE = 20 * 1024 * 1024


    /**
     * 创建目录
     * @param dirName 目录名
     */
    suspend fun createDirectory(dirName: String): File? {
        return withContext(dispatcher) {
            var directory: File? = null
            try {
                val file = File().apply {
                    //设置文件（夹）名称。
                    fileName = dirName
                    //设置文件（夹）MIME类型。
                    mimeType = MimeType.FOLDER
                }
                directory = drive.files().create(file).execute()
            } catch (e: Exception) {
                Log.e(TAG, "createDirectory $dirName  error: $e")
            }
            directory
        }
    }

    /**
     * 上传文件，该方式支持上传进度保存功能
     * （用户上传过程中断网等，下次再上传同一个文件时会接着上次的进度上传）
     *
     * @param filePath 文件路径
     * @param parentId 文件所在目录ID
     */
    suspend fun createFile(
        text: MutableLiveData<String>,
        parentId: String,
        filePath: String,
        cloudFiles: List<File>
    ) {
        withContext(dispatcher) {
            try {
                val file = java.io.File(filePath)
                val sha256 = HEX.encodeHexString(SHA256.digest(file), true)
                Log.e(TAG, "createFile:  ${file.name}   sha256 = $sha256")
                cloudFiles.forEach {
                    Log.e(TAG, "createFile: 云sha256  ${it.sha256}")
                    if (sha256.equals(it.sha256, ignoreCase = true)) {
                        Log.e(TAG, "createFile: 云端已存在该文件 ${file.name}")
                        return@withContext
                    }
                }

                text.postValue("")
                val fileContent = FileContent(null, file)

                val content = File()
                    .setFileName(file.name)
                    .setParentFolder(Collections.singletonList(parentId))


                val request = drive.files().create(content, fileContent)

                // 设置上传方式，默认使用断点续传。当文件小于20M时可以使用直接上传，即设置为true。
                request.mediaHttpUploader.isDirectUploadEnabled =
                    file.length() < DIRECT_UPLOAD_MAX_SIZE

                val createResult = request.execute()
                if (createResult != null) {
                    Log.e(TAG, "createFile:  ${file.name}")
                    text.postValue(text.value + "\ncreateFile:  ${file.name}")
                }

            } catch (e: Exception) {
                Log.e(TAG, "createFile: ", e)
                text.postValue(text.value + "createFile: $e")
            }
        }
    }

    /**
     * 永久删除文件（目录）
     *
     * @param file 待删除的文件或目录
     */
    suspend fun deleteFile(file: File) {
        withContext(dispatcher) {
            try {
                val deleteFile = drive.files().delete(file.id)
                deleteFile.execute()
                Log.d(TAG, "deleteFile ${file.fileName} success")
            } catch (ex: IOException) {
                Log.e(TAG, "deleteFile ${file.fileName} error: $ex")
            }
        }
    }

    /**
     * 获取文件元数据
     *
     * @param fileId 文件Id
     */
    suspend fun getFileMetadata(fileId: String): File? {
        return withContext(dispatcher) {
            var file: File? = null
            try {
                val request = drive.files()[fileId]
                request.fields = "*"
                file = request.execute()
            } catch (e: java.lang.Exception) {
                Log.e(TAG, "get metadata error: $e")
            }
            file
        }
    }

    /**
     * 下载文件元数据及文件实体
     */
    suspend fun downLoadFile(file: File): Boolean {
        return withContext(dispatcher) {
            try {
                val size = file.getSize()
                val get = drive.files()[file.id]
                get.form = "media"
                //配置下载器
                get.mediaHttpDownloader.run {
                    val isDirectDownload = size < DIRECT_DOWNLOAD_MAX_SIZE
                    setContentRange(0, size - 1).isDirectDownloadEnabled = isDirectDownload
                    progressListener = MediaHttpDownloaderProgressListener { mediaHttpDownloader ->
                        // 下载子线程调用此方法处理下载进度。
                        val progress = mediaHttpDownloader.progress
                        Log.d(TAG, "downLoadFile: ${file.fileName}  $progress")
                    }
                }
                //保存位置的路径
                val imagePath = "/storage/emulated/0/DCIM/Camera/"
                val f = java.io.File(imagePath + file.fileName)
                get.executeContentAndDownloadTo(FileOutputStream(f))
                Log.d(TAG, "downLoadFile success : $file")
                true
            } catch (e: Exception) {
                Log.e(TAG, "downLoadFile exception: ", e)
                false
            }
        }
    }

    /**
     * 搜索文件
     *
     * @param query 查询参数，详见Q查询语句
     * @param orderBy 排序字段
     * @param pageSize 分页大小
     * @param fields 设置请求返回时携带内容
     */
    suspend fun getFileList(
        query: String,
        orderBy: String,
        pageSize: Int,
        fields: String
    ): List<File>? {
        return withContext(dispatcher) {
            var fileList: MutableList<File>? = null
            try {
                val request = drive.files().list()
                var cursor: String?
                fileList = ArrayList()
                do {
                    val result = request.setQueryParam(query)
                        .setOrderBy(orderBy)
                        .setPageSize(pageSize)
                        .setFields(fields)
                        .execute()
                    for (file in result.files) {
                        fileList.add(file)
                    }
                    cursor = result.nextCursor
                    request.cursor = cursor
                } while (!StringUtils.isNullOrEmpty(cursor))
            } catch (e: Exception) {
                Log.e(TAG, "executeFilesList exception: $e")
            }
            fileList
        }
    }


    /**
     * 注册指定文件变化通知channel，开启指定文件变化通知
     *
     * @param fileId 文件id
     */
    suspend fun filesWatch(fileId: String) {
        withContext(dispatcher) {
            try {
                val content = Channel()
                content.type = "web_hook"
                content.url = "https://xxxx.com" //文件变化通知服务器地址，开发者需搭建该服务器
                val request = drive.files().subscribe(fileId, content)
                val channel: Channel = request.execute()
                Log.d(TAG, "filesWatch 成功")
            } catch (e: java.lang.Exception) {
                Log.e(TAG, "Exception" + e.cause)
            }
        }
    }


    /**
     * 上传文件，该方式支持上传进度保存功能
     * （用户上传过程中断网等，下次再上传同一个文件时会接着上次的进度上传）
     *
     * @param filePath 文件路径
     * @param parentId 文件所在目录ID
     * @param thumbnailImageBuffer 缩略图数据
     * @param thumbnailMimeType 缩略图文件类型
     */
    suspend fun createFile(
        file: java.io.File,
        parentId: String,
        thumbnailImageBuffer: ByteArray,
        thumbnailMimeType: String
    ): File? {
        return withContext(dispatcher) {
            try {
                val fileContent = FileContent(null, file)
                // 设置缩略图数据
                val thumbnail = Thumbnail()
                thumbnail.content = Base64.encodeBase64String(thumbnailImageBuffer)
                thumbnail.mimeType = thumbnailMimeType
                val contentExtra = ContentExtras()
                contentExtra.thumbnail = thumbnail

                val content = File()
                    .setFileName(file.name)
                    .setParentFolder(Collections.singletonList(parentId))
                    .setContentExtras(contentExtra)

                val request = drive.files().create(content, fileContent)

                var isDirectUpload = false
                // 文件小于20M时可以使用直接上传
                if (file.length() < DIRECT_UPLOAD_MAX_SIZE) {
                    isDirectUpload = true
                }
                // 设置上传方式，默认使用断点续传。当文件小于20M时可以使用直接上传，即设置为true。
                request.mediaHttpUploader.isDirectUploadEnabled = isDirectUpload
                request.execute()
            } catch (e: java.lang.Exception) {
                Log.e(TAG, "createFile: ", e)
                null
            }
        }
    }

    /**
     * 通过InputStream创建文件
     *
     * @param fileName 文件名
     * @param inputStream 媒体流
     * @param parentId 文件所在目录ID
     * @param mimeType MIME类型，如image/jpeg、video/mp4等
     * @param inputStreamLength stream流长度
     */
    suspend fun createFile(
        fileName: String,
        inputStream: InputStream,
        parentId: String,
        mimeType: String,
        inputStreamLength: Long
    ) {
        withContext(dispatcher) {
            try {
                val streamContent = InputStreamContent(mimeType, inputStream)
                streamContent.length = inputStreamLength
                val content = File()
                    .setFileName(fileName)
                    .setParentFolder(Collections.singletonList(parentId))
                drive.files().create(content, streamContent).execute()
            } catch (e: java.lang.Exception) {
                Log.e(TAG, "createFile exception: $e")
            }
        }
    }

    /**
     * 拷贝文件到指定目录
     *
     * @param file 待拷贝的文件
     * @param dstDir 拷贝文件到指定的目录
     */
    suspend fun copyFile(
        file: File,
        dstDir: ArrayList<String>
    ) {
        withContext(dispatcher) {
            try {
                val copyFile = File()
                if (file.fileName == null) {
                    Log.e(TAG, "copyFile arguments error")
                    return@withContext
                }
                val formatter = SimpleDateFormat("yyyyMMdd_HHmmss")
                val suffix = formatter.format(Date())
                copyFile.fileName = file.fileName + "_copy" + "_" + suffix
                copyFile.description = "copyFile"
                copyFile.parentFolder = dstDir
                copyFile.favorite = true
                copyFile.editedTime = DateTime(System.currentTimeMillis())
                val copyFileReq = drive.files().copy(file.id, copyFile)
                copyFileReq.fields = "*"
                val result = copyFileReq.execute()
                Log.e(TAG, "copyFile 成功: ${result.fileName}")
            } catch (ex: IOException) {
                Log.e(TAG, "copyFile error: $ex")
            }
        }
    }

    /**
     * 修改文件（目录），通过MIMEType区分是文件还是目录
     *
     * @param file 待修改的文件（目录）
     */
    suspend fun updateFile(file: File) {
        withContext(dispatcher) {
            try {
                val updateFile = File()
                updateFile.setFileName(file.fileName + "_update")
                    .setDescription("update folder").favorite = true
                drive.files().update(file.id, updateFile).execute()
                Log.d(TAG, "updateFile 成功")
            } catch (e: java.lang.Exception) {
                Log.e(TAG, "updateFile error: $e")
            }
        }
    }

    /**
     * 修改文件
     *
     * @param oldFile 源文件
     * @param newFilePath 更新后文件路径
     */
    suspend fun updateFile(oldFile: File, newFilePath: String) {
        withContext(dispatcher) {
            try {
                if (TextUtils.isEmpty(newFilePath)) {
                    Log.e(TAG, "updateFile error, need to create file.")
                    return@withContext
                }
                val sourceFile = java.io.File(newFilePath)
                val fileContent = FileContent(null, sourceFile)
                val updateFile = File()
                updateFile.setFileName(oldFile.fileName + "_update")
                    .setDescription("update folder").favorite = true
                val update = drive.files().update(oldFile.id, updateFile, fileContent)
                var isDirectUpload = false
                // 文件小于20M时可以使用直接上传
                if (sourceFile.length() < DIRECT_UPLOAD_MAX_SIZE) {
                    isDirectUpload = true
                }
                // 设置上传方式，默认使用断点续传。当文件小于20M时可以使用直接上传，即设置为true。
                update.mediaHttpUploader.isDirectUploadEnabled = isDirectUpload
                update.execute()
                Log.d(TAG, "updateFile 成功")
            } catch (e: java.lang.Exception) {
                Log.e(TAG, "updateFile error: $e")
            }
        }
    }

    companion object {
        //下限制载请求最大并发量10
        private val dispatcher = Executors.newFixedThreadPool(10).asCoroutineDispatcher()
    }
}